package cn.sc.summer.feign.config;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.Header;
import cn.sc.summer.feign.retry.RetryerConfig;
import cn.sc.summer.constant.project.ProjectConstant;
import cn.sc.summer.constant.token.TokenConstant;
import cn.sc.summer.exception.exceptions.CommonException;
import cn.sc.summer.resource.constant.PropertyConstant;
import cn.sc.summer.resource.factory.YamlPropertySourceFactory;
import cn.sc.summer.token.encrypt.TokenUtil;
import cn.sc.summer.token.encrypt.properties.EncryptProperties;
import cn.sc.summer.token.enums.RequestTypeEnum;
import cn.sc.summer.token.service.EncryptService;
import feign.Logger;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import feign.Retryer;
import feign.codec.Encoder;
import feign.form.spring.SpringFormEncoder;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.cloud.openfeign.support.SpringEncoder;
import org.springframework.context.annotation.*;
import org.springframework.http.MediaType;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * 类名：feign接口实现请求头传递
 *
 * @author a-xin
 * @date 2023/8/29 13:41
 */
@Slf4j
@Configuration
@RequiredArgsConstructor
@PropertySource(value = PropertyConstant.FEIGN_PATH, factory = YamlPropertySourceFactory.class)
public class FeignRequestConfig implements RequestInterceptor {

    @Value("${retry.maxAttempts:3}")
    private Integer MAX_ATTEMPTS;

    @Value("${retry.backoff:500}")
    private Long BACK_OFF;

    @Resource
    private EncryptProperties encryptProperties;

    @Resource
    private EncryptService encryptService;

    final ObjectFactory<HttpMessageConverters> messageConverters;

    @Bean
    @Primary
    @Scope("prototype")
    public Encoder feignEncoder() {
        return new SpringFormEncoder(new SpringEncoder(messageConverters));
    }

    @Override
    public void apply(RequestTemplate requestTemplate) {
        try {

            //添加请求头
            Map<String, String> headers = getHeaders();
            for (String headerName : headers.keySet()) {
                if (headerName.equalsIgnoreCase(TokenConstant.ACCESS_TOKEN)) {
                    requestTemplate.header(headerName, headers.get(headerName));
                }
            }

            //判断feign请求数据参数类型，如果是上传文件，必须在feign上指定请求类型，否则报错
            boolean encrypt;
            Collection<String> contentTypes = requestTemplate.headers().get(Header.CONTENT_TYPE.getValue());
            if (CollUtil.isEmpty(contentTypes)) {
                encrypt = Boolean.TRUE;
            } else {
                String contentType = new ArrayList<>(contentTypes).get(0);
                encrypt = contentType.equalsIgnoreCase(MediaType.APPLICATION_JSON_VALUE)
                        || contentType.equalsIgnoreCase(MediaType.APPLICATION_JSON_UTF8_VALUE);
            }

            //加密请求参数
            if (ObjectUtil.isNotNull(requestTemplate.body()) && encrypt) {
                int length = requestTemplate.body().length;
                if (length > 0) {
                    String body = new String(requestTemplate.body(), StandardCharsets.UTF_8);
                    if (CharSequenceUtil.isNotBlank(body)) {
                        if (encryptProperties.getEncryptFeign()) {
                            requestTemplate.header(Header.CONTENT_TYPE.getValue(), MediaType.APPLICATION_JSON_VALUE);
                            requestTemplate.body(encryptService.encryptFeignData(body).getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8);
                        } else {
                            requestTemplate.body(body.getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8);
                        }
                    }
                }
            }

            requestTemplate.header(TokenConstant.API_KEY_REQUEST_HEADER, TokenUtil.getApiEncryptKey(16, ProjectConstant.SERVER_NAME, RequestTypeEnum.OPEN_FEIGN));
            requestTemplate.header(TokenConstant.REQUEST_TYPE, RequestTypeEnum.OPEN_FEIGN.getType());

        } catch (Exception e) {
            throw new CommonException(e.getMessage(), e);
        }
    }

    @Bean
    public Logger.Level feignLoggerLevel() {
        return Logger.Level.BASIC;
    }

    @Bean
    public Retryer retryer() {
        log.info("==> Openfeign maximum number of retries: {} times, Retry interval: {} ms", MAX_ATTEMPTS, BACK_OFF);
        return new RetryerConfig(MAX_ATTEMPTS, BACK_OFF, 0);
    }

    /**
     * 获取请求头信息
     *
     * @return 所有请求头信息
     */
    private Map<String, String> getHeaders() {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        Map<String, String> map = new LinkedHashMap<>();
        Enumeration<String> enumeration = request.getHeaderNames();
        while (enumeration.hasMoreElements()) {
            String key = enumeration.nextElement();
            String value = request.getHeader(key);
            map.put(key, value);
        }
        return map;
    }

}

